package com.example.smartwaterapi.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.smartwaterapi.entity.User;
import com.example.smartwaterapi.entity.WaterRecord;
import com.example.smartwaterapi.mapper.WaterRecordMapper;
import com.example.smartwaterapi.service.UserService;
import com.example.smartwaterapi.service.WaterRecordService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 饮水记录服务实现类
 */
@Slf4j
@Service
public class WaterRecordServiceImpl extends ServiceImpl<WaterRecordMapper, WaterRecord> implements WaterRecordService {

    @Autowired
    private UserService userService;
    
    /**
     * 批处理大小
     */
    private static final int BATCH_SIZE = 100;

    /**
     * 添加饮水记录
     */
    @Override
    @Transactional
    @CacheEvict(value = {"waterRecord", "waterStat"}, keyGenerator = "waterRecordCacheKeyGenerator")
    public boolean addRecord(WaterRecord record) {
        // 设置记录时间相关字段
        if (record.getRecordTime() == null) {
            // 使用系统当前时间（服务器时区）
            record.setRecordTime(LocalDateTime.now());
        } else {
            // 记录原始时间，便于调试
            log.debug("原始记录时间: {}", record.getRecordTime());
            
            try {
                // 确保时区正确，如果时间格式不包含时区信息，假定为东八区
                // 检查是否有时区信息
                if (record.getRecordTime().toString().endsWith("Z")) {
                    // UTC时间，需要转换到东八区
                    LocalDateTime utcTime = record.getRecordTime();
                    // 转换到东八区 (UTC+8)
                    LocalDateTime beijingTime = utcTime.plusHours(8);
                    record.setRecordTime(beijingTime);
                    log.info("时间已从UTC转换为北京时间: {} -> {}", utcTime, beijingTime);
                }
            } catch (Exception e) {
                log.warn("时间转换处理异常", e);
            }
        }
        
        // 手动设置 record_date 和 hour 字段，因为它们现在是普通列
        record.setRecordDate(record.getRecordTime().toLocalDate());
        record.setHour(record.getRecordTime().getHour());
        
        // 设置创建时间
        record.setCreatedAt(LocalDateTime.now());

        log.info("正在添加饮水记录: userId={}, amount={}ml, drinkType={}, time={}", 
                record.getUserId(), record.getAmount(), record.getDrinkType(), record.getRecordTime());
        
        boolean result = save(record);
        
        // 异步更新统计数据
        if (result) {
            // 从记录时间获取日期用于统计，不再使用实体中的 record_date
            LocalDate recordDate = record.getRecordTime().toLocalDate();
            updateStatisticsAsync(record.getUserId(), recordDate);
            log.debug("添加饮水记录成功，用户ID: {}, 记录ID: {}, 饮水量: {}ml", record.getUserId(), record.getId(), record.getAmount());
        } else {
            log.warn("添加饮水记录失败，用户ID: {}", record.getUserId());
        }
        
        return result;
    }

    /**
     * 批量添加饮水记录
     */
    @Override
    @Transactional
    @CacheEvict(value = {"waterRecord", "waterStat"}, allEntries = true)
    public boolean batchAddRecords(List<WaterRecord> records) {
        if (records == null || records.isEmpty()) {
            log.warn("批量添加饮水记录失败：记录列表为空");
            return false;
        }
        
        log.debug("开始批量添加饮水记录，记录数量: {}", records.size());
        
        // 处理每条记录的时间字段
        for (WaterRecord record : records) {
            if (record.getRecordTime() == null) {
                record.setRecordTime(LocalDateTime.now());
            }
            
            // 手动设置 record_date 和 hour 字段
            record.setRecordDate(record.getRecordTime().toLocalDate());
            record.setHour(record.getRecordTime().getHour());
            record.setCreatedAt(LocalDateTime.now());
        }
        
        // 分批处理，避免一次性处理过多数据
        boolean result = true;
        int totalProcessed = 0;
        
        try {
            for (int i = 0; i < records.size(); i += BATCH_SIZE) {
                int endIndex = Math.min(i + BATCH_SIZE, records.size());
                List<WaterRecord> batch = records.subList(i, endIndex);
                int affected = baseMapper.batchInsert(batch);
                result = result && (affected > 0);
                totalProcessed += affected;
                
                log.debug("批量处理进度: {}/{}, 本批次处理: {}", endIndex, records.size(), affected);
            }
            
            // 如果有记录，异步更新统计数据
            if (result && !records.isEmpty()) {
                // 获取第一条记录的用户ID和日期，用于更新统计
                WaterRecord firstRecord = records.get(0);
                // 从记录时间获取日期用于统计，不再使用实体中的record_date
                LocalDate recordDate = firstRecord.getRecordTime().toLocalDate();
                updateStatisticsAsync(firstRecord.getUserId(), recordDate);
                log.info("批量添加饮水记录成功，用户ID: {}, 总记录数: {}, 实际插入: {}", 
                        firstRecord.getUserId(), records.size(), totalProcessed);
            }
        } catch (Exception e) {
            log.error("批量添加饮水记录异常", e);
            result = false;
        }
        
        return result;
    }

    /**
     * 获取用户指定日期的饮水记录
     */
    @Cacheable(value = "waterRecord", key = "'user_daily_' + #userId + '_' + #date.toString()", unless = "#result.isEmpty()")
    @Override
    public List<WaterRecord> getUserDailyRecords(Long userId, LocalDate date) {
        log.debug("获取用户每日饮水记录，用户ID: {}, 日期: {}", userId, date);
        return baseMapper.selectList(new LambdaQueryWrapper<WaterRecord>()
                .eq(WaterRecord::getUserId, userId)
                .eq(WaterRecord::getRecordDate, date)
                .orderByAsc(WaterRecord::getRecordTime));
    }

    /**
     * 分页获取用户饮水记录
     */
    @Override
    public Page<WaterRecord> getUserRecords(Long userId, Integer pageNum, Integer pageSize) {
        log.debug("分页获取用户饮水记录，用户ID: {}, 页码: {}, 每页数量: {}", userId, pageNum, pageSize);
        Page<WaterRecord> page = new Page<>(pageNum, pageSize);
        LambdaQueryWrapper<WaterRecord> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(WaterRecord::getUserId, userId)
               .orderByDesc(WaterRecord::getRecordTime);
        return page(page, wrapper);
    }

    /**
     * 按日期范围分页获取用户饮水记录
     */
    @Override
    public Page<WaterRecord> getUserRecordsByDateRange(Long userId, LocalDate startDate, LocalDate endDate, int pageNum, int pageSize) {
        Page<WaterRecord> page = new Page<>(pageNum, pageSize);
        
        LambdaQueryWrapper<WaterRecord> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(WaterRecord::getUserId, userId)
                  .ge(WaterRecord::getRecordDate, startDate)
                  .le(WaterRecord::getRecordDate, endDate)
                  .orderByDesc(WaterRecord::getRecordDate, WaterRecord::getRecordTime);
        
        return baseMapper.selectPage(page, queryWrapper);
    }

    /**
     * 获取用户每日饮水总量
     */
    @Cacheable(value = "waterStat", key = "'daily_total_' + #userId + '_' + #date.toString()", unless = "#result == null")
    @Override
    public Integer getUserDailyTotal(Long userId, LocalDate date) {
        log.debug("获取用户每日饮水总量，用户ID: {}, 日期: {}", userId, date);
        return baseMapper.getDailyTotal(userId, date);
    }
    
    /**
     * 获取用户指定日期范围内每日饮水量
     */
    @Override
    @Cacheable(value = "waterStat", key = "'dailyRange:' + #userId + ':' + #startDate + ':' + #endDate", unless = "#result == null || #result.isEmpty()")
    public List<Map<String, Object>> getUserDailyTotalsByDateRange(Long userId, LocalDate startDate, LocalDate endDate) {
        log.debug("获取用户日期范围饮水统计，用户ID: {}, 开始日期: {}, 结束日期: {}", userId, startDate, endDate);
        return baseMapper.getDailyTotalsByDateRange(userId, startDate, endDate);
    }
    
    /**
     * 获取用户指定日期的饮水类型分布
     */
    @Override
    @Cacheable(value = "waterStat", key = "'drinkType:' + #userId + ':' + #date", unless = "#result == null || #result.isEmpty()")
    public List<Map<String, Object>> getUserDrinkTypeDistribution(Long userId, LocalDate date) {
        log.debug("获取用户饮水类型分布，用户ID: {}, 日期: {}", userId, date);
        return baseMapper.getDrinkTypeDistribution(userId, date);
    }
    
    /**
     * 获取用户指定日期的每小时饮水量
     */
    @Override
    @Cacheable(value = "waterStat", key = "'hourly:' + #userId + ':' + #date", unless = "#result == null || #result.isEmpty()")
    public List<Map<String, Object>> getUserHourlyDistribution(Long userId, LocalDate date) {
        log.debug("获取用户每小时饮水分布，用户ID: {}, 日期: {}", userId, date);
        return baseMapper.getHourlyDistribution(userId, date);
    }

    /**
     * 获取用户饮水目标完成率
     */
    @Override
    @Cacheable(value = "waterStat", key = "'goalCompletion:' + #userId + ':' + #date", unless = "#result == 0")
    public double getUserDailyGoalCompletion(Long userId, LocalDate date) {
        log.debug("获取用户饮水目标完成率，用户ID: {}, 日期: {}", userId, date);
        
        // 获取用户每日目标
        User user = userService.getById(userId);
        if (user == null || user.getDailyGoal() == null || user.getDailyGoal() <= 0) {
            log.warn("获取用户饮水目标失败，用户ID: {}, 用户不存在或目标未设置", userId);
            return 0.0;
        }
        
        // 获取用户当日饮水总量
        Integer dailyTotal = getUserDailyTotal(userId, date);
        if (dailyTotal == null) {
            return 0.0;
        }
        
        // 计算完成率，最高100%
        double completion = Math.min(1.0, (double) dailyTotal / user.getDailyGoal()) * 100;
        log.debug("用户饮水目标完成率: {}%, 用户ID: {}, 日期: {}, 目标: {}ml, 实际: {}ml", 
                String.format("%.2f", completion), userId, date, user.getDailyGoal(), dailyTotal);
        
        return completion;
    }

    /**
     * 获取用户连续达标天数
     */
    @Override
    public int getUserConsecutiveGoalDays(Long userId) {
        log.debug("获取用户连续达标天数，用户ID: {}", userId);
        
        // 获取用户每日目标
        User user = userService.getById(userId);
        if (user == null || user.getDailyGoal() == null || user.getDailyGoal() <= 0) {
            log.warn("获取用户连续达标天数失败，用户ID: {}, 用户不存在或目标未设置", userId);
            return 0;
        }
        
        int consecutiveDays = 0;
        LocalDate date = LocalDate.now();
        
        // 从今天开始往前查询
        while (true) {
            Integer dailyTotal = getUserDailyTotal(userId, date);
            
            // 如果没有记录或未达标，则中断
            if (dailyTotal == null || dailyTotal < user.getDailyGoal()) {
                break;
            }
            
            consecutiveDays++;
            date = date.minusDays(1);
        }
        
        log.debug("用户连续达标天数: {}天, 用户ID: {}", consecutiveDays, userId);
        return consecutiveDays;
    }
    
    /**
     * 异步更新用户统计数据
     * 使用异步方式避免影响主流程性能
     */
    @Async("dataProcessExecutor")
    public CompletableFuture<Void> updateStatisticsAsync(Long userId, LocalDate date) {
        log.debug("开始异步更新用户统计数据，用户ID: {}, 日期: {}", userId, date);
        
        try {
            // 这里可以添加更新DailyStats表的逻辑
            // 例如：dailyStatsService.updateDailyStats(userId, date);
            
            // 如果是月末或月初，还可以更新月度统计
            int dayOfMonth = date.getDayOfMonth();
            int lastDayOfMonth = date.lengthOfMonth();
            if (dayOfMonth == 1 || dayOfMonth == lastDayOfMonth) {
                log.debug("触发月度统计更新，用户ID: {}, 年份: {}, 月份: {}", userId, date.getYear(), date.getMonthValue());
                // 更新月度统计
                // monthlyStatsService.updateMonthlyStats(userId, date.getYear(), date.getMonthValue());
            }
            
            // 检查是否达成成就
            // waterAchievementService.checkAchievements(userId, date);
            
            log.debug("异步更新用户统计数据完成，用户ID: {}, 日期: {}", userId, date);
        } catch (Exception e) {
            // 记录异常但不影响主流程
            log.error("异步更新统计数据失败，用户ID: {}, 日期: {}", userId, date, e);
        }
        
        return CompletableFuture.completedFuture(null);
    }

    /**
     * 获取用户饮水统计概览 - 使用并发优化
     */
    @Override
    public Map<String, Object> getUserWaterStatistics(Long userId, LocalDate date) {
        Map<String, Object> result = new ConcurrentHashMap<>();
        
        // 并行获取不同统计数据，提高响应速度
        CompletableFuture<Void> dailyTotalFuture = CompletableFuture
            .supplyAsync(() -> getUserDailyTotal(userId, date))
            .thenAccept(total -> result.put("dailyTotal", total != null ? total : 0));
        
        CompletableFuture<Void> goalCompletionFuture = CompletableFuture
            .supplyAsync(() -> getUserDailyGoalCompletion(userId, date))
            .thenAccept(completion -> result.put("goalCompletion", completion));
        
        CompletableFuture<Void> consecutiveDaysFuture = CompletableFuture
            .supplyAsync(() -> getUserConsecutiveGoalDays(userId))
            .thenAccept(days -> result.put("consecutiveDays", days));
        
        // 等待所有异步任务完成
        CompletableFuture.allOf(
            dailyTotalFuture, 
            goalCompletionFuture, 
            consecutiveDaysFuture
        ).join();
        
        return result;
    }
    
    /**
     * 获取用户所有时间的饮水总量
     */
    @Override
    public Long getUserAllTimeTotal(Long userId) {
        // 安全地获取聚合结果
        Object result = baseMapper.getUserAllTimeTotal(userId);
        
        // 处理空值
        if (result == null) {
            return 0L;
        }
        
        // 安全的类型转换
        if (result instanceof Integer) {
            return ((Integer) result).longValue();
        } else if (result instanceof Long) {
            return (Long) result;
        } else if (result instanceof Number) {
            return ((Number) result).longValue();
        } else {
            // 作为最后的保障，尝试字符串转换
            try {
                return Long.valueOf(result.toString());
            } catch (Exception e) {
                log.error("无法将聚合结果转换为Long: {}", result, e);
                return 0L;
            }
        }
    }

    // 线程安全的日期格式化器
    private static final ThreadLocal<SimpleDateFormat> dateFormatter = 
        ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
}
